package org.activequant.data.util.series;

import org.activequant.core.domainmodel.data.Candle;
import org.activequant.core.domainmodel.data.MarketDataEntity;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.data.TradeIndication;
import org.activequant.core.types.TimeStamp;
import org.activequant.data.retrieval.ISeriesDataIteratorSource;

/**
 * Transforms market data series into a lagged series by adding a fixed amount of time
 * to the timestamp of every data item. Lag is controlled by {@link #getLagNanoseconds() lagNanoseconds}
 * property. Positive lag shifts series forward in time (positive addition to the timestamp makes
 * every data item "newer"). Negative lag shifts series back in time. <br/>
 * LaggedSeriesDataIteratorSource&lt;T extends MarketDataEntity&lt;T&gt;&gt; extends TransformingDataIteratorSourceBase&lt;T&gt;.<br/>
 * Holds the following associated variables:
 * <ul>
 * <li>lagNanoseconds(long)</li>
 * </ul>
 * Holds the following inherited variables:
 * <ul>
 * <li>vendor(String)</li>
 * <li>source(ISeriesDataIteratorSource&lt;T&gt;)</li>
 * </ul>
 * <p>
 * <b>History:</b><br>
 *  - [13.01.2008] Created (Mike Kroutikov)<br>
 *
 *  @author Mike Kroutikov
 */
public class LaggedSeriesDataIteratorSource<T extends MarketDataEntity<T>> extends TransformingDataIteratorSourceBase<T> {
	/**
	 * private long lagNanoseconds = 0L;
	 */
	private long lagNanoseconds = 0L;
	/**
	 * constructs a LaggedSeriesDataIteratorSource&lt;T extends MarketDataEntity&lt;T&gt;&gt;(extends TransformingDataIteratorSourceBase&lt;T&gt;) using the given
	 * vendor(String) and source(ISeriesDataIteratorSource&lt;T&gt;) to set its inherited vendor(String) and source(ISeriesDataIteratorSource&lt;T&gt;)
	 * @param vendor
	 * @param source
	 */
	public LaggedSeriesDataIteratorSource(String vendor, ISeriesDataIteratorSource<T> source) {
		super(vendor, source);
	}
	/**
	 * <strong>1.</strong> If the given item(T) is a Quote, it creates and returns a clone Quote whose timeStamp(TimeStamp) is increased by the associated lagNanoseconds(long).<br/>
	 * <strong>2.</strong> If the given item(T) is a TradeIndication, it creates and returns a clone TradeIndication whose timeStamp(TimeStamp) is increased by the associated lagNanoseconds(long).<br/>
	 * <strong>3.</strong> If the given item(T) is a Candle, it creates and returns a clone Candle whose timeStamp(TimeStamp) (and if set highStamp(TimeStamp) and lowStamp(TimeStamp)) are increased by the associated lagNanoseconds(long).
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected T transformDataItem(T item) {
		
		TimeStamp stamp = new TimeStamp(item.getTimeStamp().getNanoseconds() + lagNanoseconds);
		
		if(Quote.class.isAssignableFrom(item.getClass())) {
			Quote q = (Quote) (Object) item;
			
			T newItem = (T) (Object) q.clone();
			newItem.setTimeStamp(stamp);
			
			return newItem;
			
		} else if(TradeIndication.class.isAssignableFrom(item.getClass())) {
			TradeIndication t = (TradeIndication) (Object) item;
			
			T newItem = (T) (Object) t.clone();
			newItem.setTimeStamp(stamp);
			
			return newItem;

		} else if(Candle.class.isAssignableFrom(item.getClass())) {
			Candle c = (Candle) (Object) item;
			
			Candle newItem = c.clone();
			newItem.setTimeStamp(stamp);
			
			if(newItem.getHighTimeStamp() != null) {
				TimeStamp high = new TimeStamp(newItem.getHighTimeStamp().getNanoseconds() + lagNanoseconds);
				newItem.setHighTimeStamp(high);
			}
			
			if(newItem.getLowTimeStamp() != null) {
				TimeStamp low = new TimeStamp(newItem.getLowTimeStamp().getNanoseconds() + lagNanoseconds);
				newItem.setLowTimeStamp(low);
			}

			return (T) (Object) newItem;
			
		} else {
			throw new AssertionError("unexpected data item type: " + item.getClass() + ", " + item);
		}
	}

	/**
	 * Lag value in nanoseconds.<br/>
	 * returns the associated lagNanoseconds(long)
	 * @return lag nanoseconds.
	 */
	public long getLagNanoseconds() {
		return lagNanoseconds;
	}
	
	/**
	 * Sets lag value in nanoseconds.<br/>
	 * sets the associated lagNanoseconds(long) with the given val(long)
	 * @param val lag in nanoseconds.
	 */
	public void setLagNanoseconds(long val) {
		lagNanoseconds = val;
	}
}
